/*
 * XInput1_3-dev - Replacement of xinput library to help with creating
 *                 drivers/debugging application
 *
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.
 */

#include "stdafx.h"

#include "XInputCommon.h"
#include "DeviceInfo.h"
#include "XInputCore.h"
#include "DeviceEnum.h"
#include "DriverComm.h"
#include "DeviceList.h"

DWORD XInputReturnCodeFromHRESULT(HRESULT hResult)
{
	if ( SUCCEEDED(hResult) )
	{
		if ( hResult == S_FALSE )
			return ERROR_DEVICE_NOT_CONNECTED;

		return ERROR_SUCCESS;
	}
	else
	{
		if ( hResult == E_OUTOFMEMORY )
		{
			return ERROR_OUTOFMEMORY;
		}

		if ( hResult == E_INVALIDARG )
			return ERROR_BAD_ARGUMENTS;
	}
	return hResult;
}

XINPUT_EXPORT DWORD WINAPI XInputGetState(DWORD dwUserIndex, XINPUT_STATE* pState)
{
	DWORD dwError = 0;
	if ( dwUserIndex < XUSER_MAX_COUNT )
	{
		if ( pState )
		{
			LPVOID lpArgs[1];

			lpArgs[0] = (LPVOID)pState;

			HRESULT hResult = XInputCore::ProcessAPIRequest(dwUserIndex, DeviceInfo::g_pfnGetStateDispatcher, lpArgs, 1);
			dwError = XInputReturnCodeFromHRESULT(hResult);

			if ( dwError == ERROR_SUCCESS )
				pState->Gamepad.wButtons &= 0xFBFF;
		}
		else
		{
			dwError = ERROR_BAD_ARGUMENTS;
		}
	}
	else
	{
		dwError = ERROR_BAD_ARGUMENTS;
	}

	return dwError;
}

XINPUT_EXPORT DWORD WINAPI XInputGetStateEx(DWORD dwUserIndex, XINPUT_STATE* pState)
{
	DWORD dwError = 0;
	if ( dwUserIndex < XUSER_MAX_COUNT )
	{
		if ( pState )
		{
			LPVOID lpArgs[1];

			lpArgs[0] = (LPVOID)pState;

			HRESULT hResult = XInputCore::ProcessAPIRequest(dwUserIndex, DeviceInfo::g_pfnGetStateDispatcher, lpArgs, 1);
			dwError = XInputReturnCodeFromHRESULT(hResult);
		}
		else
		{
			dwError = ERROR_BAD_ARGUMENTS;
		}
	}
	else
	{
		dwError = ERROR_BAD_ARGUMENTS;
	}

	return dwError;
}


XINPUT_EXPORT DWORD WINAPI XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration)
{
	DWORD dwError = ERROR_SUCCESS;
	if ( dwUserIndex < XUSER_MAX_COUNT )
	{
		if ( pVibration )
		{
			LPVOID lpArgs[1];

			lpArgs[0] = (LPVOID)pVibration;

			HRESULT hResult = XInputCore::ProcessAPIRequest(dwUserIndex, DeviceInfo::g_pfnSetVibrationDispatcher, lpArgs, 1);
			dwError = XInputReturnCodeFromHRESULT(hResult);
		}
		else
		{
			dwError = ERROR_BAD_ARGUMENTS;
		}
	}
	else
	{
		dwError = ERROR_BAD_ARGUMENTS;
	}
	return dwError;
}

XINPUT_EXPORT DWORD WINAPI XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES* pCapabilities)
{
	DWORD dwError = ERROR_SUCCESS;
	if ( dwUserIndex < XUSER_MAX_COUNT )
	{
		if ( dwFlags && dwFlags != 1 )
		{
			dwError = ERROR_BAD_ARGUMENTS;
		}
		else
		{
			if ( pCapabilities )
			{
				LPVOID lpArgs[1];

				lpArgs[0] = (LPVOID)pCapabilities;

				HRESULT hResult = XInputCore::ProcessAPIRequest(dwUserIndex, DeviceInfo::g_pfnGetCapabilitiesDispatcher, lpArgs, sizeof(lpArgs) / sizeof(LPVOID));
				dwError = XInputReturnCodeFromHRESULT(hResult);
				if ( dwError == ERROR_SUCCESS )
					pCapabilities->Gamepad.wButtons &= 0xFBFF;
			}
			else
			{
				dwError = ERROR_BAD_ARGUMENTS;
			}
		}
	}
	else
	{
		dwError = ERROR_BAD_ARGUMENTS;
	}
	return dwError;
}

XINPUT_EXPORT void WINAPI XInputEnable(BOOL enable)
{
	XInputCore::EnableCommunications(enable != 0);
}

XINPUT_EXPORT DWORD WINAPI XInputGetDSoundAudioDeviceGuids(DWORD dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid)
{
	return ERROR_DEVICE_NOT_CONNECTED;
}

XINPUT_EXPORT DWORD WINAPI XInputGetBatteryInformation(DWORD dwUserIndex, BYTE devType, XINPUT_BATTERY_INFORMATION* pBatteryInformation)
{
	DWORD dwError = ERROR_SUCCESS;
	if ( dwUserIndex < XUSER_MAX_COUNT )
	{
		if ( pBatteryInformation )
		{
			pBatteryInformation->BatteryLevel = 0;
			pBatteryInformation->BatteryType = 0;

			bool bNoSuccess = true;

			if ( devType == 1 )
			{
				dwError = ERROR_DEVICE_NOT_CONNECTED;

				QuickDriverEnum DriverEnum;
				bool bContinue = DriverEnum.Restart();

				DEVICE_INFO DeviceInfo = {0};

				while ( bContinue )
				{
					HANDLE hDevice = INVALID_HANDLE_VALUE;
					bContinue = DriverEnum.GetNext(&hDevice);
					if ( hDevice != INVALID_HANDLE_VALUE )
					{
						if ( DeviceInfo::MinFillFromInterface(hDevice, &DeviceInfo) )
						{
							if ( DeviceInfo.wVersion >= 258 )
							{
								if ( SUCCEEDED(DriverComm::GetBatteryInformation(&DeviceInfo, 1, pBatteryInformation)) )
								{
									bNoSuccess = false;
									bContinue = false;
									devType = 0;
								}
							}
						}
						CloseHandle(hDevice);
					}
				}
			}

			if ( bNoSuccess == true )
			{
				LPVOID lpArgs[2];

				lpArgs[0] = (LPVOID)&devType;
				lpArgs[1] = (LPVOID)pBatteryInformation;

				HRESULT hResult = XInputCore::ProcessAPIRequest(dwUserIndex, DeviceInfo::g_pfnGetBatteryInformationDispatcher, lpArgs, 2);
				dwError = XInputReturnCodeFromHRESULT(hResult);
			}
		}
		else
		{
			return ERROR_BAD_ARGUMENTS;
		}
	}
	else
	{
		return ERROR_BAD_ARGUMENTS;
	}

	return dwError;
}

XINPUT_EXPORT DWORD WINAPI XInputGetKeystroke(DWORD dwUserIndex, DWORD dwReserved, PXINPUT_KEYSTROKE pKeystroke)
{
	DWORD dwError = ERROR_SUCCESS;
	if ( dwUserIndex < XUSER_MAX_COUNT || dwUserIndex == XUSER_INDEX_ANY )
	{
		if ( pKeystroke )
		{
			LPVOID lpArgs[2];

			lpArgs[0] = (LPVOID)&dwReserved;
			lpArgs[1] = (LPVOID)pKeystroke;

			if ( dwUserIndex == XUSER_INDEX_ANY )
			{
				dwError = ERROR_EMPTY;
				for ( DWORD i = 0; i < XUSER_MAX_COUNT; i++ )
				{
					XInputCore::ProcessAPIRequest(i, DeviceInfo::g_pfnGetKeystrokeDispatcher, lpArgs, 3);
					if ( dwError == ERROR_SUCCESS)
					{
						pKeystroke->UserIndex = (BYTE)i;
						break;
					}
				}
				if ( dwError )
				  dwError = ERROR_EMPTY;
			}
			else
			{
				HRESULT hResult = XInputCore::ProcessAPIRequest(dwUserIndex, DeviceInfo::g_pfnGetKeystrokeDispatcher, lpArgs, 3);
				if(SUCCEEDED(hResult))
					pKeystroke->UserIndex = (BYTE)dwUserIndex;
				else
					dwError = XInputReturnCodeFromHRESULT(hResult);
			}
		}
		else
		{
			dwError = 160;
		}
  }
  else
  {
    dwError = 160;
  }
  return dwError;
}

// Oridinal: 102
DWORD WINAPI XInputWaitForGuideButton(DWORD dwUserIndex, HANDLE hEvent, PXINPUT_LISTEN_STATE pListenState)
{
	DWORD dwError = ERROR_SUCCESS;
	if ( dwUserIndex < XUSER_MAX_COUNT )
	{
		if ( pListenState )
		{
			if(hEvent == INVALID_HANDLE_VALUE)
				hEvent = 0;

			LPVOID lpArgs[2];

			lpArgs[0] = (LPVOID)&hEvent;
			lpArgs[1] = (LPVOID)pListenState;

			HRESULT hResult = XInputCore::ProcessAPIRequest(dwUserIndex, DeviceInfo::g_pfnWaitForGuideButton, lpArgs, 2);
			dwError = XInputReturnCodeFromHRESULT(hResult);
		}
		else
		{
			dwError = ERROR_BAD_ARGUMENTS;
		}
	}
	else
	{
		dwError = ERROR_BAD_ARGUMENTS;
	}

	return dwError;
}

// Oridinal: 103
DWORD WINAPI XInputCancelGuideButtonWait(DWORD dwUserIndex)
{
	DWORD dwError = ERROR_SUCCESS;
	if ( dwUserIndex < XUSER_MAX_COUNT )
	{
		HRESULT hResult = XInputCore::ProcessAPIRequest(dwUserIndex, DeviceInfo::g_pfnCancelGuideButtonWait, 0, 0);
		dwError = XInputReturnCodeFromHRESULT(hResult);
	}
	else
	{
		dwError = ERROR_BAD_ARGUMENTS;
	}

	return dwError;
}

DWORD WINAPI XInputPowerOffController(DWORD dwUserIndex)
{
	DWORD dwError = ERROR_SUCCESS;
	if ( dwUserIndex < XUSER_MAX_COUNT )
	{
		HRESULT hResult = XInputCore::ProcessAPIRequest(dwUserIndex, DeviceInfo::g_pfnPowerOffController, 0, 0);
		dwError = XInputReturnCodeFromHRESULT(hResult);
	}
	else
	{
		dwError = ERROR_BAD_ARGUMENTS;
	}

	return dwError;
}